package com.seleniti.enpareja.android.control.factory;

import java.net.CookieManager;

import org.ioc.commons.flowcontrol.eventbus.EventHandler;
import org.ioc.commons.flowcontrol.eventbus.IsEventBus;
import org.ioc.commons.flowcontrol.messageboxfactory.MessageBoxFactory;
import org.ioc.commons.flowcontrol.notificator.Notificator;
import org.ioc.commons.flowcontrol.placecontroller.PlaceController;
import org.ioc.commons.flowcontrol.taskcontroller.TaskController;
import org.ioc.commons.i18n.DateTimeFormatter;
import org.ioc.commons.i18n.NumberFormatter;
import org.ioc.commons.impl.android.flowcontrol.logincontroller.LoginControllerAccountAuthenticator;
import org.ioc.commons.impl.android.flowcontrol.notificator.AndroidNotificator;
import org.ioc.commons.impl.android.flowcontrol.notificator.AndroidNotificator.NotificationIconProvider;
import org.ioc.commons.impl.android.flowcontrol.placecontroller.AndroidPlaceController;
import org.ioc.commons.impl.android.flowcontrol.taskcontroller.AndroidTimerTaskController;
import org.ioc.commons.impl.android.i18n.AndroidDateTimeFormatter;
import org.ioc.commons.impl.android.i18n.AndroidNumberFormatter;
import org.ioc.commons.impl.android.integration.dataaccess.localstorage.SharedPreferencesLocalClientStorage;
import org.ioc.commons.impl.android.integration.service.japtsy.CookieManagerProvider;
import org.ioc.commons.impl.android.ui.HasContextWrapper;
import org.ioc.commons.integration.dataaccess.localstorage.LocalClientStorage;
import org.ioc.commons.model.clientfactory.ClientFactoryProvider;
import org.ioc.commons.model.globaldata.GlobalDataController;

import android.app.Application;
import android.content.Context;
import android.view.HasContext;

import com.seleniti.enpareja.android.AppShellActivity;
import com.seleniti.enpareja.android.R;
import com.seleniti.enpareja.android.control.controller.AndroidEnParejaAppControllerImpl;
import com.seleniti.enpareja.android.control.controller.EnParejaExceptionControllerImpl;
import com.seleniti.enpareja.android.control.controller.EnParejaLoginController;
import com.seleniti.enpareja.android.control.controller.MessageBoxFactoryImpl;
import com.seleniti.enpareja.android.control.navigation.AppPlaceMapper;
import com.seleniti.enpareja.android.model.integration.engine.service.impl.japtsy.EnParejaServiceManagerImpl;
import com.seleniti.enpareja.controller.common.GlobalData;
import com.seleniti.enpareja.controller.common.LocalData;
import com.seleniti.enpareja.controller.common.domain.LoggedUserInfo;
import com.seleniti.enpareja.controller.control.controller.LoginFailedCause;
import com.seleniti.enpareja.controller.control.controller.LoginParams;
import com.seleniti.enpareja.controller.control.factory.ClientFactory;
import com.seleniti.enpareja.controller.control.factory.EnParejaExceptionController;
import com.seleniti.enpareja.controller.control.place.EnParejaPlace;
import com.seleniti.enpareja.controller.model.integration.service.EnParejaServiceManager;
import com.seleniti.enpareja.controller.ui.main.AppShell;
import com.seleniti.enpareja.controller.ui.widget.SliderPanel.BoardType;

public class ClientFactoryImpl implements ClientFactory, CookieManagerProvider, HasContext {

	private static String websiteUrl;

	private final Application theApp;

	private ClientFactoryProvider<AndroidEnParejaAppControllerImpl> appControllerProvider;

	private ClientFactoryProvider<AndroidPlaceController> placeControllerProvider;

	private EnParejaServiceManagerImpl serviceManager;

	private NumberFormatter numberFormatter;

	private DateTimeFormatter dateTimeFormatter;

	private GlobalDataController<GlobalData> globalDataController;

	private SharedPreferencesLocalClientStorage<LocalData> localClientStorage;

	private ClientFactoryProvider<TaskController> taskController;

	private ClientFactoryProvider<MessageBoxFactory> messageBoxFactoryProvider;

	private EnParejaExceptionControllerImpl exceptionController;

	private AppShell appShell;

	private CookieManager cookieManager = new CookieManager();

	private final HasContext appContextProvider;

	private HasContext shellContext;

	private ClientFactoryProvider<LoginControllerAccountAuthenticator<?, ?, ?, ?>> authenticatorProvider;

	private EnParejaLoginController loginController;

	private AppPlaceMapper placeMapper;

	private ClientFactoryProvider<? extends Notificator<NotificationIcons>> notificatorProvider;

	public ClientFactoryImpl(Application theApp) {
		this.theApp = theApp;
		this.appContextProvider = HasContextWrapper.from(this.theApp);

	}

	@Override
	public ClientFactoryProvider<AndroidPlaceController> getPlaceController() {
		if (this.placeControllerProvider == null) {
			this.placeMapper = new AppPlaceMapper();
			this.placeControllerProvider = new ClientFactoryProvider<AndroidPlaceController>() {

				@Override
				public AndroidPlaceController forThisObject(Object object) {
					AndroidPlaceController placeController = new AndroidPlaceController(
							HasContext.Helper.getFromObject(object), placeMapper);
					placeController.eventbus().bindEvent(PlaceController.Event.PlaceChanged,
							new EventHandler<PlaceController.Event>() {

								@Override
								public void handleEvent(Object sender,
										org.ioc.commons.flowcontrol.placecontroller.PlaceController.Event event,
										Object... params) {
									EnParejaPlace place = (EnParejaPlace) (params[0]);
									BoardType boardType = placeMapper.getBoardType(place);
									if (boardType != null) {
										getShell().mainContainer().setBoardType(boardType);
									}
								}
							});
					return placeController;
				}
			};

		}
		return this.placeControllerProvider;
	}

	@Override
	public ClientFactoryProvider<AndroidEnParejaAppControllerImpl> getAppController() {

		if (this.appControllerProvider == null) {
			this.appControllerProvider = new ClientFactoryProvider<AndroidEnParejaAppControllerImpl>() {

				@Override
				public AndroidEnParejaAppControllerImpl forThisObject(Object object) {
					return new AndroidEnParejaAppControllerImpl(HasContext.Helper.getFromObject(object),
							ClientFactoryImpl.this);
				}

			};
		}

		return this.appControllerProvider;
	}

	@Override
	public IsEventBus getGlobalEventBus() {
		return getAppController().forThisObject(this).eventBus();
	}

	@Override
	public GlobalDataController<GlobalData> getGlobalDataController() {
		if (this.globalDataController == null) {
			this.globalDataController = new GlobalDataController<GlobalData>();
		}
		return this.globalDataController;
	}

	@Override
	public DateTimeFormatter getDateTimeFormatter() {
		return (this.dateTimeFormatter == null ? (this.dateTimeFormatter = new AndroidDateTimeFormatter())
				: this.dateTimeFormatter);
	}

	@Override
	public NumberFormatter getNumberFormatter() {
		return (this.numberFormatter == null ? (this.numberFormatter = new AndroidNumberFormatter())
				: this.numberFormatter);
	}

	@Override
	public LocalClientStorage<LocalData> getLocalClientStorage() {
		return (this.localClientStorage == null) ? (this.localClientStorage = new SharedPreferencesLocalClientStorage<LocalData>(
				this.theApp.getSharedPreferences("EnParejaSettings", Context.MODE_PRIVATE))) : this.localClientStorage;
	}

	@Override
	public EnParejaExceptionController getExceptionController() {
		return (this.exceptionController == null) ? (this.exceptionController = new EnParejaExceptionControllerImpl(
				this)) : this.exceptionController;
	}

	@Override
	public ClientFactoryProvider<TaskController> getTaskController() {
		if (this.taskController == null) {
			this.taskController = new ClientFactoryProvider<TaskController>() {

				@Override
				public TaskController forThisObject(Object object) {
					return new AndroidTimerTaskController(HasContext.Helper.getFromObject(object));
				}

			};
		}
		return this.taskController;
	}

	@Override
	public ClientFactoryProvider<MessageBoxFactory> getMessageBoxFactory() {
		if (this.messageBoxFactoryProvider == null) {
			this.messageBoxFactoryProvider = new ClientFactoryProvider<MessageBoxFactory>() {

				@Override
				public MessageBoxFactory forThisObject(Object object) {
					return new MessageBoxFactoryImpl(HasContext.Helper.getFromObject(object));
				}

			};
		}

		return this.messageBoxFactoryProvider;
	}

	@Override
	public EnParejaServiceManager getServiceManager() {
		if (this.serviceManager == null) {
			this.serviceManager = new EnParejaServiceManagerImpl(this.theApp.getApplicationContext(), this);
			this.serviceManager.setModuleBaseUrl(getWebsiteServletUrl());
			this.serviceManager.setRemoteServiceRelativePath("dataServices/japtsy");
		}
		return this.serviceManager;
	}

	@Override
	public AppShell getShell() {
		return this.appShell;
	}

	public void setAppShell(AppShellActivity appShell) {
		this.appShell = appShell;
		this.shellContext = appShell;
	}

	public void setCookieManager(CookieManager cookieManager) {
		this.cookieManager = cookieManager;
	}

	@Override
	public CookieManager getCookieManager() {
		return this.cookieManager;
	}

	public HasContext getAppContextProvider() {
		return appContextProvider;
	}

	@Override
	public Context getContext() {
		return (this.shellContext != null ? this.shellContext : this.appContextProvider).getContext();
	}

	public ClientFactoryProvider<LoginControllerAccountAuthenticator<?, ?, ?, ?>> getAuthenticatorProvider() {
		if (this.authenticatorProvider == null) {
			this.authenticatorProvider = new ClientFactoryProvider<LoginControllerAccountAuthenticator<?, ?, ?, ?>>() {

				@Override
				public LoginControllerAccountAuthenticator<?, ?, ?, ?> forThisObject(Object object) {
					return new LoginControllerAccountAuthenticator<LoggedUserInfo, LoginFailedCause, LoginParams, String>(
							HasContext.Helper.getFromObject(object), getLoginController());
				}

			};
		}
		return this.authenticatorProvider;
	}

	@Override
	public EnParejaLoginController getLoginController() {
		return ClientFactoryImpl.this.loginController == null ? (ClientFactoryImpl.this.loginController = new EnParejaLoginController(
				ClientFactoryImpl.this, getServiceManager().serviceFactory().securityManagementService(),
				getGlobalDataController())) : ClientFactoryImpl.this.loginController;
	}

	public static void setWebsiteUrl(String url) {
		websiteUrl = url;
	}

	public static String getWebsiteUrl() {
		if (websiteUrl == null) {
			throw new IllegalStateException("Required web site url not set yet");
		}

		return websiteUrl;
	}

	public static String getWebsiteServletUrl() {
		return websiteUrl != null ? websiteUrl + "/enpareja/" : null;
	}

	@Override
	public ClientFactoryProvider<? extends Notificator<NotificationIcons>> getNotificator() {
		if (this.notificatorProvider == null) {
			this.notificatorProvider = new ClientFactoryProvider<Notificator<NotificationIcons>>() {

				@Override
				public Notificator<NotificationIcons> forThisObject(Object object) {
					NotificationIconProvider<NotificationIcons> iconProvider = new NotificationIconProvider<NotificationIcons>() {

						@Override
						public int getIconUrl(NotificationIcons icon) {
							switch(icon) {
							case OK_ICON:
								return R.drawable.ic_action_accept;
							case CANCEL_ICON:
								return android.R.drawable.ic_menu_close_clear_cancel;
							case CALENDAR_ICON:
								return R.drawable.calendar_icon;
							case APP_ICON:
							default:
								return R.drawable.ic_launcher;
							}
						}

					};
					return new AndroidNotificator<NotificationIcons>(HasContext.Helper.getFromObject(object), iconProvider,
							R.drawable.ic_launcher);
				}

			};
		}
		return this.notificatorProvider;
	}
}
